package testing.module;

import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Container;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Properties;
import java.util.Random;

import javax.imageio.ImageIO;

import utilities.display.imgs.DrawImage;
import utilities.display.shapes.definedshapes.DrawCircle;
import utilities.display.shapes.definedshapes.DrawPolygon;
import utilities.display.shapes.definedshapes.DrawRectangle;
import utilities.manager.definederunner.GameRunner;
import utilities.module.definedmods.GameScreen;

public class SteelWings extends GameScreen 
{
	/**
	 * 
	 */
	private static final long serialVersionUID = -866705978799415276L;
	
	public enum STEEL_WING_STATES
	{
		START,
		SELECT_SHIP,
		PRACTICE_PLAY,
		LOCAL_PLAY,
		ONLINE_PLAY,
		CREDITS
	}
	
	protected STEEL_WING_STATES state = null;
	
	/*******************************************************************
	 * 
	 * TITLE SCREEN DATA
	 * 
	 *******************************************************************/
	
	protected BufferedImage bgImg;
	protected BufferedImage cliffImg;
	
	protected DrawImage titleTag;
	protected DrawImage subtitleImg;
	protected DrawImage moonImg;

	protected ArrayList<DrawImage> buttons;
	protected ArrayList<DrawImage> clouds;

	protected DrawImage practiceButton;
	protected DrawImage localButton;
	protected DrawImage onlineButton;
	protected DrawImage creditsButton;
	
	protected Rectangle highlightButton;
	
	protected double buttonScale = .4;
	protected int buttonXStart = 0;
	protected int buttonYStart = 100;
	protected int buttonDX = 0;
	protected int buttonDY = 65;
	
	/*******************************************************************
	 * 
	 * PRACTICE PLAY DATA
	 * 
	 *******************************************************************/
	
	protected DrawPolygon leftWall;
	protected DrawPolygon rightWall;
	protected int finishLinePos;
	
	protected int totalLeftWallLength;
	protected int totalRightWallLength;
	
	protected final int MIN_LENGTH = 10;
	protected final int MAX_LENGTH = 100;
	
	protected int maxLength = 250; //20000
	
	protected final int MIN_DEPTH = 1;
	protected final int MAX_DEPTH_CHANGE = 50;

	protected int lastLeftDepth;
	protected int lastRightDepth;
	
	protected int maxLeftDepth;
	protected int maxRightDepth;
	
	protected int MIN_OPEN_SPACE;
	protected int MAX_OPEN_SPACE;
	
	protected int distanceTravelled;
	
	protected Random rand;
	
	protected boolean raceOver = false;
	protected String raceOverText = null;
	protected long returnToIntroTime = -1;
	protected Font raceOverFont = new Font("SansSerif", Font.BOLD, 75);

	/**
	 * 
	 */
	protected DrawRectangle playerArea;
	
	protected static final int BOX_SIZE = 50;
	
	/**
	 * 
	 */
	//protected DrawCircle aimingCircle;

	protected int aimX = 0;
	protected int aimY = 0;
	protected static final int CURSOR_RADIUS = 12;
	
	/**
	 * 
	 */
	protected int currScreenSpeed = 2;
	
	protected boolean moveLeft;
	protected boolean moveRight;
	protected boolean moveUp;
	protected boolean moveDown;

	protected int playerXSpeed = 0;
	protected int playerXAccel = 2;
	protected int playerXDrag = 1;
	protected int playerXSpeedMax = 30;
	
	protected int playerYSpeed = 0;
	protected int playerYAccel = 2;
	protected int playerYDrag = 1;
	protected int playerYSpeedMax = 30;
	
	protected boolean boxUp = false;
	protected boolean boxDown = false;
	protected boolean boxLeft = false;
	protected boolean boxRight = false;

	protected int minBoxX;
	protected int minBoxY;
	protected int maxBoxX;
	protected int maxBoxY;
	
	protected boolean collisionDetected = false;
	
	protected double energy = 200.0d;
	
	protected static final NumberFormat LIFE_FORMAT = new DecimalFormat("0000.0");
	protected static final double SCRAPE_DAMAGE = 0.5d;
	protected static final double ENERGY_LOSS = 0.1d;
	
	protected double score = 0.0d;
	
	/*******************************************************************
	 * 
	 * CONSTRUCTORS
	 * 
	 *******************************************************************/
	
	public SteelWings(GameRunner runner) throws IOException 
	{
		super(runner);
		
		// Initial state is...
		state = STEEL_WING_STATES.START;
		initializeIntroState();
	}
	
	/*******************************************************************
	 * 
	 * OVERRIDEN METHODS
	 * 
	 *******************************************************************/
	
	/**
	 * 
	 */
	public void drawMe(Graphics g)
	{
		Graphics2D g2 = (Graphics2D)g;
		
		Color c = g2.getColor();
		Composite r = g2.getComposite();
		
		if(STEEL_WING_STATES.START.equals(state))
		{
			drawIntroState(g);
		}
		else if(STEEL_WING_STATES.PRACTICE_PLAY.equals(state))
		{
			drawPracticeState(g);
		}
		
		g2.setColor(c);
		g2.setComposite(r);
	}
	
	/**
	 * 
	 */
	public void update(Properties globals)
	{
		if(STEEL_WING_STATES.PRACTICE_PLAY.equals(state))
		{
			updatePracticeState(globals);
		}
	}
	
	/**
	 * 
	 */
	public void keyPressed(KeyEvent e)
	{
		if(STEEL_WING_STATES.PRACTICE_PLAY.equals(state))
		{
			onKeyEvent_Practice(e);
		}
	}
	
	public void keyReleased(KeyEvent e)
	{
		if(STEEL_WING_STATES.PRACTICE_PLAY.equals(state))
		{
			onKeyEvent_Practice(e);
		}
	}
	
	/**
	 * 
	 */
	public void mouseMoved(MouseEvent e)
	{		
		if(STEEL_WING_STATES.START.equals(state))
		{
			onMouseEvent_Start(e);
		}
		else if(STEEL_WING_STATES.PRACTICE_PLAY.equals(state))
		{
			onMouseEvent_Practice(e);
		}
	}
	
	/**
	 * 
	 */
	public void mouseDragged(MouseEvent e)
	{		
		if(STEEL_WING_STATES.START.equals(state))
		{
			onMouseEvent_Start(e);
		}
		else if(STEEL_WING_STATES.PRACTICE_PLAY.equals(state))
		{
			onMouseEvent_Practice(e);
		}
	}
	
	/**
	 * 
	 */
	public void mouseClicked(MouseEvent e)
	{
		if(STEEL_WING_STATES.START.equals(state))
		{
			onMouseEvent_Start(e);
		}
	}
	
	/**
	 * 
	 */
	public void mouseReleased(MouseEvent e)
	{
		if(STEEL_WING_STATES.START.equals(state))
		{
			onMouseEvent_Start(e);
		}
	}
	
	/**
	 * 
	 */
	public void mouseExited(MouseEvent e)
	{		
		if(STEEL_WING_STATES.START.equals(state))
		{
			onMouseEvent_Start(e);
		}
	}
	
	/*******************************************************************
	 * 
	 * INTRO SCREEN
	 * 
	 *******************************************************************/
	
	/**
	 * 
	 */
	private final void initializeIntroState() throws IOException
	{
		buttonXStart = this.getWidth() - 200;
		buttonYStart = this.getHeight() - 300;
		
		bgImg = ImageIO.read(new File("resources//imgs//title//background.png"));
		cliffImg = ImageIO.read(new File("resources//imgs//title//cliff.png"));
		
		titleTag = new DrawImage(ImageIO.read(new File("resources//imgs//title//wingslogocropped.png")), null, 0.4, 5, 5);
		subtitleImg = new DrawImage(ImageIO.read(new File("resources//imgs//title//subtitle.png")), null, 0.3, 6, (int)(2 + titleTag.getArea().y + titleTag.getArea().getHeight()));
		moonImg = new DrawImage(ImageIO.read(new File("resources//imgs//title//moons.png")), AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .45f), 0.65, 350, 30);

		// Get button images
		buttons = new ArrayList<DrawImage>();
		
		// Practice Choice
		buttons.add(practiceButton = new DrawImage(ImageIO.read(new File("resources//imgs//title//buttons//PRACTICE.png")), null, buttonScale, buttonXStart + buttonDX, buttonYStart));
		
		// Local Choice
		buttons.add(localButton = new DrawImage(ImageIO.read(new File("resources//imgs//title//buttons//LOCAL.png")), null, buttonScale, buttonXStart + buttonDX, buttonYStart + buttonDY));
		
		// Online Choice
		buttons.add(onlineButton = new DrawImage(ImageIO.read(new File("resources//imgs//title//buttons//ONLINE.png")), null, buttonScale, buttonXStart + buttonDX * 2, buttonYStart + buttonDY * 2));
		
		// Credits Choice
		buttons.add(creditsButton = new DrawImage(ImageIO.read(new File("resources//imgs//title//buttons//CREDITS.png")), null, buttonScale, buttonXStart + buttonDX * 3, buttonYStart + buttonDY * 3));

		clouds = new ArrayList<DrawImage>();
		
		// Load up clouds
		//for(int i = 1; i <= 4; i++)
		//for(int i = 3; i <= 3; i++)
		//{
			//BufferedImage cloud = ImageIO.read(new File("resources//imgs//title//cloud" + i + ".png"));
			//clouds.add(new DrawImage(cloud, AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .55f), 1, 20* i, 20));
		//}
//		BufferedImage cloud1 = ImageIO.read(new File("resources//imgs//title//cloud1.png"));
//		BufferedImage cloud2 = ImageIO.read(new File("resources//imgs//title//cloud2.png"));		
//		BufferedImage cloud3 = ImageIO.read(new File("resources//imgs//title//cloud3.png"));
//		BufferedImage cloud4 = ImageIO.read(new File("resources//imgs//title//cloud4.png"));
	}
	
	/**
	 * 
	 * @param g
	 */
	private final void drawIntroState(Graphics g)
	{
		Graphics2D g2 = (Graphics2D)g;
		
		// Draw background scenery
		
		// Background sky
		g2.drawImage(bgImg, 0, 0, bgImg.getWidth(), bgImg.getHeight(), null);
		
		// Moon
		moonImg.drawMe(g2);
		
		// Clouds
		for(DrawImage img : clouds)
		{
			if(img != null) img.drawMe(g2);
		}
		
		// Cliff
		g2.drawImage(cliffImg, 0, 0, cliffImg.getWidth(), cliffImg.getHeight(), null);
		
		// Draw UI
		titleTag.drawMe(g2);
		subtitleImg.drawMe(g2);
		
		// Draw buttons
		for(DrawImage button : buttons)
		{
			if(button != null) button.drawMe(g2);
		}
		
		// Highlight button
		if(highlightButton != null)
		{
			Color c = g2.getColor();
			g2.setColor(Color.red);
			g2.setStroke(new BasicStroke(4));
			g2.draw(highlightButton);
			g2.setColor(c);
		}
		
		// Draw some damn circle.
//		int mx = getRunner().getMouseX();
//		int my = getRunner().getMouseY();
//		Color c = g2.getColor();
//		g2.setColor(Color.red);
//		g2.setStroke(new BasicStroke(2));
//		g2.drawOval(mx - 5, my - 5, 10, 10);
//		g2.setColor(c);
	}
	
	/**
	 * 
	 * @param e
	 */
	private void onMouseEvent_Start(MouseEvent e)
	{
		if(e != null)
		{
			int mx = getRunner().getMouseX();
			int my = getRunner().getMouseY();
			
			if(MouseEvent.MOUSE_MOVED == e.getID())
			{
				highlightButton = null;

				for(DrawImage button : buttons)
				{
					if(button != null && button.getArea().contains(mx, my))
						highlightButton = button.getArea();
				}
			}
			else if(MouseEvent.MOUSE_DRAGGED == e.getID() && highlightButton != null && !highlightButton.contains(mx, my))
			{
				highlightButton = null;
			}
			else if(MouseEvent.MOUSE_EXITED == e.getID())
			{
				highlightButton = null;
			}
			else if(MouseEvent.MOUSE_RELEASED == e.getID())
			{
				if(practiceButton.getArea().equals(highlightButton))
				{
					initializePracticeState();
					this.state = STEEL_WING_STATES.PRACTICE_PLAY;
				}
			}
//			else
//			{
//				System.out.println("WARNING: No applicable mouse event detected.");
//			}
		}
	}
	
	/*******************************************************************
	 * 
	 * PRACTICE SCREEN
	 * 
	 *******************************************************************/
	
	/**
	 * 
	 */
	private final void initializePracticeState()
	{
		Container parent = getRunner().getTopLevelAncestor();
		
		playerArea = new DrawRectangle(((parent.getWidth() - BOX_SIZE) / 2), getRunner().getHeight() - BOX_SIZE - 30, BOX_SIZE, BOX_SIZE, Color.green, new BasicStroke(5), null, Color.blue, null);
		//aimingCircle = new DrawCircle(playerArea.getShape().x, playerArea.getShape().y, CIRCLE_RADIUS, Color.black, new BasicStroke(2), null, null, null);
		aimX = playerArea.getShape().x;
		aimY = playerArea.getShape().y;
		
		energy = 200.0d;
		score = 0.0d;
		
		leftWall = new DrawPolygon(Color.black, null, null, new Color(139, 69, 19), AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.75f));
		rightWall = new DrawPolygon(Color.black, null, null, new Color(139, 69, 19), AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.75f));
		
		lastLeftDepth = 10;
		lastRightDepth = 10;
		
		distanceTravelled = 0;
		
		totalLeftWallLength = 0;
		totalRightWallLength = 0;
		
		rand = new Random();
		
		MIN_OPEN_SPACE = BOX_SIZE + 2;
		MAX_OPEN_SPACE = getWidth();
		
		maxLeftDepth = maxRightDepth = (MAX_OPEN_SPACE - MIN_OPEN_SPACE) / 2;
		
		// Bounded area that the box can move around in
		minBoxX = 0; //Math.abs(box.getDrawDX());
		minBoxY = 0; //Math.abs(box.getDrawDY());

		maxBoxX = (int) (parent.getWidth() - BOX_SIZE) - parent.getInsets().right - parent.getInsets().left;
		maxBoxY = (int) (parent.getHeight() - BOX_SIZE) - parent.getInsets().bottom - parent.getInsets().top;
		
		finishLinePos = -(maxLength + MAX_LENGTH);
		
		createCanyonWalls();
	}
	
	/**
	 * 
	 */
	private void createCanyonWalls()
	{
		// Each side of the canyon must be as long as the track itself.
		// Each side of the canyon starts at the left and right corners.
		
		leftWall.addPoint(0, 0);
		rightWall.addPoint(getWidth(), 0);
		
		// Randomly generate points for each side until both walls fill the entire race length.
		//while(leftWallLength < maxLength)
		while(totalLeftWallLength < maxLength || totalRightWallLength < maxLength)
		{
			// Determine length of the corridor
			int wallLength = rand.nextInt(MAX_LENGTH - MIN_LENGTH + 1) + MIN_LENGTH;
			
			// Continue building the left wall if there is enough space left
			if(totalLeftWallLength < maxLength)
			{
				// Record remaining wall length
				int remainingLeftWallLength = wallLength;
				
				// For each point...
				while(remainingLeftWallLength >= MIN_LENGTH)
				{
					// Determine length of random corridor point
					int currLeftWallLength = rand.nextInt(Math.max(remainingLeftWallLength - MIN_LENGTH + 1, 1)) + MIN_LENGTH;
					
					// Determine depth of random corridor point
					lastLeftDepth = Math.min(maxLeftDepth, Math.max(MIN_DEPTH, lastLeftDepth + rand.nextInt(MAX_DEPTH_CHANGE) - rand.nextInt(MAX_DEPTH_CHANGE)));
					
					// Add new corridor point to the left wall
					leftWall.addPoint(lastLeftDepth, -(totalLeftWallLength + currLeftWallLength));
					
					// Increase left wall length to new length of progress
					totalLeftWallLength += currLeftWallLength;
					
					// Subtract left length used from remaining left wall length
					remainingLeftWallLength -= currLeftWallLength;
				}
			}
			
			// Continue building the right wall if there is enough space left
			if(totalRightWallLength < maxLength)
			{
				// Record remaining wall length
				int remainingRightWallLength = wallLength;
				
				// For each point...
				while(remainingRightWallLength >= MIN_LENGTH)
				{
					// Determine length of random corridor
					int currRightWallLength = rand.nextInt(Math.max(remainingRightWallLength - MIN_LENGTH + 1, 1)) + MIN_LENGTH;
					
					// Determine depth of random corridor
					lastRightDepth = Math.min(maxRightDepth, Math.max(MIN_DEPTH, lastRightDepth + rand.nextInt(MAX_DEPTH_CHANGE) - rand.nextInt(MAX_DEPTH_CHANGE)));
	
					// Add new "points" of corridor to the right wall
					rightWall.addPoint(getWidth() - lastRightDepth, -(totalRightWallLength + currRightWallLength));
					
					// Increase left wall length to new length of progress
					totalRightWallLength += currRightWallLength;
					
					// Subtract left length used from remaining left wall length
					remainingRightWallLength -= currRightWallLength;
				}
			}
			
			// Shift available area to the left or right by a small amount
			int corridorShift = rand.nextInt(5) * (rand.nextBoolean() ? 1 : -1);
			
			maxLeftDepth += corridorShift;
			maxRightDepth -= corridorShift;
			
			// Make sure possible depths stay within boundaries
			if(maxLeftDepth > getWidth() - MIN_OPEN_SPACE) maxLeftDepth = getWidth() - MIN_OPEN_SPACE;
			else if(maxLeftDepth < MIN_DEPTH) maxLeftDepth = MIN_DEPTH;

			if(maxRightDepth > getWidth() - MIN_OPEN_SPACE) maxRightDepth = getWidth() - MIN_OPEN_SPACE;
			else if(maxRightDepth < MIN_DEPTH) maxRightDepth = MIN_DEPTH;
		}
		
		// Last point of each wall should be MAX_LENGTH after supposed max distance
		leftWall.addPoint(0, -(maxLength + MAX_LENGTH));
		rightWall.addPoint(getWidth(), -(maxLength + MAX_LENGTH));
	}
	
	/**
	 * 
	 * @param g
	 */
	private final void drawPracticeState(Graphics g)
	{
		Graphics2D g2 = (Graphics2D)g;
		
		// Draw finish line once the player is close enough
		if(finishLinePos >= 0) drawFinishLine(g2);
		
		// Draw player
		playerArea.drawMe(g2);

		// Draw walls
		leftWall.drawMe(g2);
		rightWall.drawMe(g2);
		
		// Draw cursor
		drawAimingCursor(g2);
		
		// Draw UI
		drawStats(g2);
	}

	/**
	 * 
	 * @param g2
	 */
	private void drawAimingCursor(Graphics2D g2) 
	{
		Color c = g2.getColor();
		Stroke s = g2.getStroke();

		// Draw inbetween circles, from player to cursor
		int tinyCrsrCircleCount = 9;
		int tinyCrsrCircleRadius = 2;
		int playerOriginX = (int)(playerArea.getShape().getCenterX()); //playerArea.getShape().x + playerArea.getShape().width / 2;
		int playerOriginY = (int)(playerArea.getShape().getCenterY()); //playerArea.getShape().y + playerArea.getShape().height / 2;
		
		for(int i = 0; i < tinyCrsrCircleCount; i++)
		{
			int cx = playerOriginX + ((aimX - playerOriginX) * i / tinyCrsrCircleCount);
			int cy = playerOriginY + ((aimY - playerOriginY) * i / tinyCrsrCircleCount);
			
			g2.setColor(Color.white);
			g2.fillOval(cx - tinyCrsrCircleRadius, cy - tinyCrsrCircleRadius, tinyCrsrCircleRadius * 2, tinyCrsrCircleRadius * 2);
			
			g2.setColor(Color.black);
			g2.drawOval(cx - tinyCrsrCircleRadius, cy - tinyCrsrCircleRadius, tinyCrsrCircleRadius * 2, tinyCrsrCircleRadius * 2);
		}
				
		// Draw main cursor
		g2.setStroke(new BasicStroke(2));
		g2.setColor(Color.black);

		// horizontal bar
		g2.drawLine(aimX - CURSOR_RADIUS - 3, aimY, aimX + CURSOR_RADIUS + 3, aimY);

		// vertical bar
		g2.drawLine(aimX, aimY - CURSOR_RADIUS - 3, aimX, aimY + CURSOR_RADIUS + 3);
		
		// circle
		//aimingCircle.drawMe(g2);
		g2.drawOval(aimX - CURSOR_RADIUS, aimY - CURSOR_RADIUS, CURSOR_RADIUS * 2, CURSOR_RADIUS * 2);
		
		g2.setColor(c);
		g2.setStroke(s);
	}

	/**
	 * 
	 * @param g2
	 */
	private void drawFinishLine(Graphics2D g2) 
	{
		Color c = g2.getColor();
		Stroke s = g2.getStroke();
		
		//g2.setColor(Color.black);
		//g2.setStroke(new BasicStroke(4));
		//g2.drawLine(0, finishLinePos - 10, getWidth(), finishLinePos);
		
		// Checkerboard flag!
		// It is 11 blocks wide and 3 blocks deep
		int maxRows = 3;
		int maxCols = 13;
		int flagBlockSize = (getWidth() - getRunner().getTopLevelAncestor().getInsets().right) / maxCols;
		
		for(int row = 0; row < maxRows; row++)
		{
			g2.setColor(row % 2 == 0 ? Color.black : Color.white);
			for(int col = 0; col < maxCols; col++)
			{
				g2.fillRect(col * flagBlockSize, finishLinePos - (((row + 1) * flagBlockSize) + 10), flagBlockSize, flagBlockSize);
				g2.setColor(Color.white.equals(g2.getColor()) ? Color.black : Color.white);
			}
		}

		g2.setColor(c);
		g2.setStroke(s);
	}
	
	/**
	 * 
	 * @param g2
	 */
	private final void drawStats(Graphics2D g2)
	{
		Color c = g2.getColor();
		Font f = g2.getFont();
		
		// Draw "stats"
		g2.setColor(Color.black);
		g2.drawString("Speed: " + currScreenSpeed, 2, 13);
		g2.drawString("Status: " + (energy >= 0.01d ? (collisionDetected ? "TAKING DAMAGE" : "Stable") : "DESTROYED"), 2, 25);
		g2.drawString("Energy: " + (energy >= 0.01d ? String.format("%.1f", energy) : "X_X"), 2, 37);
		g2.drawString("Score: " + String.format("%.0f", score), 2, 49);
		g2.drawString("Finish Line: " + Math.max(0, -finishLinePos), 2, 61);
		
		// Draw race over stuff
		if(raceOver)
		{
			g2.setFont(raceOverFont);
			g2.setColor(new Color(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256)));
			g2.drawString(raceOverText, 110, 280);
			g2.drawString("" + (this.returnToIntroTime - System.currentTimeMillis() + 1000) / 1000, 220, 370);
		}
		
		g2.setColor(c);
		g2.setFont(f);
	}
	
	/**
	 * 
	 * @param e
	 */
	private void onMouseEvent_Practice(MouseEvent e)
	{
		if(e != null)
		{
			if(MouseEvent.MOUSE_MOVED == e.getID())
			{
				//aimingCircle.setLocation(getRunner().getMouseX(), getRunner().getMouseY());
				aimX = getRunner().getMouseX();
				aimY = getRunner().getMouseY();
			}
			else if(MouseEvent.MOUSE_DRAGGED == e.getID())
			{
				//aimingCircle.setLocation(getRunner().getMouseX(), getRunner().getMouseY());
				aimX = getRunner().getMouseX();
				aimY = getRunner().getMouseY();
			}
		}
	}
	
	/**
	 * 
	 * @param e
	 */
	private final void onKeyEvent_Practice(KeyEvent e)
	{
		if(e != null)
		{
			if(KeyEvent.KEY_PRESSED == e.getID())
			{
				if(e.getKeyCode() == KeyEvent.VK_UP
				   || e.getKeyCode() == KeyEvent.VK_W)
				{
					boxUp = true;
				}
				
				if(e.getKeyCode() == KeyEvent.VK_DOWN
				   || e.getKeyCode() == KeyEvent.VK_S)
				{
					boxDown = true;
				}

				if(e.getKeyCode() == KeyEvent.VK_LEFT
				   || e.getKeyCode() == KeyEvent.VK_A)
				{
					boxLeft = true;
				}

				if(e.getKeyCode() == KeyEvent.VK_RIGHT
				   || e.getKeyCode() == KeyEvent.VK_D)
				{
					boxRight = true;
				}
			}
			else if(KeyEvent.KEY_RELEASED == e.getID())
			{
				if(e.getKeyCode() == KeyEvent.VK_UP
				   || e.getKeyCode() == KeyEvent.VK_W)
				{
					boxUp = false;
				}
				
				if(e.getKeyCode() == KeyEvent.VK_DOWN
				   || e.getKeyCode() == KeyEvent.VK_S)
				{
					boxDown = false;
				}
		
				if(e.getKeyCode() == KeyEvent.VK_LEFT
				   || e.getKeyCode() == KeyEvent.VK_A)
				{
					boxLeft = false;
				}
		
				if(e.getKeyCode() == KeyEvent.VK_RIGHT
				   || e.getKeyCode() == KeyEvent.VK_D)
				{
					boxRight = false;
				}
			}
		}
	}
	
	/**
	 * 
	 * @param globals
	 */
	private void updatePracticeState(Properties globals)
	{
		movePlayer();
		
		currScreenSpeed = Math.max(1, (int)(40.0 * (getRunner().getHeight() - playerArea.getShape().y) / getRunner().getHeight()) - 4);
		
		leftWall.translate(0, currScreenSpeed);
		rightWall.translate(0, currScreenSpeed);
		finishLinePos += currScreenSpeed;
		
		// Check for collision
		collisionDetected = leftWall.getShape().intersects(playerArea.getShape()) || rightWall.getShape().intersects(playerArea.getShape());
		
		// Change the box's color, depending on whether any collision was detected or not
		playerArea.setFillColor(collisionDetected ? Color.red : Color.blue);
		
		// Is the ship taking damage?
		if(energy >= 0.01d && collisionDetected) energy -= SCRAPE_DAMAGE;
		
		// If the ship is alive... increase the score!
		if(energy >= 0.01d) score += currScreenSpeed;
		
		// Ship is slowly losing energy anyway...
		energy -= ENERGY_LOSS;
		
		if(raceOver)
		{
			if(System.currentTimeMillis() >= returnToIntroTime)
			{
				state = STEEL_WING_STATES.START;
				raceOver = false;
			}
		}
		else
		{
			if(energy < 0)
			{
				raceOver = true;
				raceOverText = "LOST...";
				returnToIntroTime = System.currentTimeMillis() + 5000;
			}
			else if(finishLinePos >= playerArea.getShape().getY())
			{
				raceOver = true;
				raceOverText = "FINISH!";
				returnToIntroTime = System.currentTimeMillis() + 5000;
			}
		}
	}

	/**
	 * 
	 */
	private void movePlayer()
	{
		// Do acceleration here
		if(boxUp) playerYSpeed -= playerYAccel;
		if(boxDown) playerYSpeed += playerYAccel;
		if(boxLeft) playerXSpeed -= playerXAccel;
		if(boxRight) playerXSpeed += playerXAccel;
		
		// Do deceleration here
		if(!boxUp && playerYSpeed < 0) playerYSpeed += playerYDrag;
		if(!boxDown && playerYSpeed > 0) playerYSpeed -= playerYDrag;
		if(!boxLeft && playerXSpeed < 0) playerXSpeed += playerXDrag;
		if(!boxRight && playerXSpeed > 0) playerXSpeed -= playerXDrag;
		
		// Do max velocity checks here
		if(playerXSpeed > playerXSpeedMax) playerXSpeed = playerXSpeedMax;
		if(playerXSpeed < -playerXSpeedMax) playerXSpeed = -playerXSpeedMax;
		if(playerYSpeed > playerYSpeedMax) playerYSpeed = playerYSpeedMax;
		if(playerYSpeed < -playerYSpeedMax) playerYSpeed = -playerYSpeedMax;
		
		// Do actual movements here
		playerArea.getShape().x += playerXSpeed;
		playerArea.getShape().y += playerYSpeed;
		
		// Keep the box within the screen boundaries
		if(playerArea.getShape().x < minBoxX) playerArea.getShape().x = minBoxX;
		if(playerArea.getShape().y < minBoxY) playerArea.getShape().y = minBoxY;
		
		if(playerArea.getShape().x >= maxBoxX) playerArea.getShape().x = maxBoxX;
		if(playerArea.getShape().y >= maxBoxY) playerArea.getShape().y = maxBoxY;
	}
}